package com.golxen.framework.netty.session.cross;



import com.golxen.framework.AgentProtocol;
import com.golxen.framework.PBPacket.PBPacket;
import com.golxen.framework.PBPacket.PBPacketUtils;
import com.golxen.framework.common.BuiltinPlugin;
import com.golxen.framework.common.server.eServerType;
import com.golxen.framework.netty.NettyServices;
import com.golxen.framework.netty.session.base.AbstractSession;
import com.golxen.framework.netty.session.base.NettyChannelSession;
import com.golxen.framework.netty.session.base.ServerLinkedGroup;
import com.golxen.protojava.LoadProto;
import com.golxen.utils.LogUtil;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CrossAgentLinkedSet {
	private static Logger logger = LogUtil.COMMON;

	private static Map<Long, Integer> agentCountMap = new ConcurrentHashMap<>();
	private static Map<Long, Map<Integer, ServerLinkedGroup>>
			agentServerLinkedSet = new ConcurrentHashMap<>();
	private static Object lockObj = new Object();

	private CrossAgentLinkedSet() {

	}

	public static void init(long serverId, int count) {
		agentCountMap.put(serverId, count);
	}

	public static ServerLinkedGroup getLinkedClient(ChannelHandlerContext ctx) {
		return ctx.channel().attr(NettyServices.LINKED_CLIENT).get();
	}

	public static boolean isContainAgentConnect(long serverId) {
		if (!agentServerLinkedSet.containsKey(serverId) || !agentCountMap.containsKey(serverId)) {
			return false;
		}
		return true;
	}

	public static boolean send(long serverId, long userId, PBPacket packet) {
		if (!isContainAgentConnect(serverId)) {
			logger.info("Can not found agent connection , serverId = " + serverId + ", code = " + packet.getCode());
			return false;
		}

		if (userId <= 0) {
			return sendSingle(serverId, packet);
		} else {

			int idx = (int) (userId % agentCountMap.get(serverId));
			ServerLinkedGroup group = agentServerLinkedSet.get(serverId).get(idx);
			if (group != null) {
				group.sendPacket(userId, packet);
			} else {
				logger.error("Can not found agent connection , serverId = " + serverId + " , userId = " + userId
						+ " code : " + packet.getCode() + " . packet forward failed." + " .idx : " + idx
						+ "  .agentLinkedSet size:" + agentServerLinkedSet.get(serverId).size());
			}
		}
		return true;
	}

	public static void sendToAll(PBPacket packet) {
		if (agentCountMap != null) {
			for (long serverId : agentCountMap.keySet()) {
				send(serverId, 1, packet);
			}
		}
	}

	private static boolean sendSingle(long serverId, PBPacket packet) {

		for (ServerLinkedGroup group : agentServerLinkedSet.get(serverId).values()) {
			group.write(packet.getPlayerId(), packet);
			return true;
		}
		return false;
	}

	public static ServerLinkedGroup addAgentLinkedGroup(long serverId, int serverIndex, NettyChannelSession session,
														int count, int linkIndex) {
		synchronized (lockObj) {
			if (!agentServerLinkedSet.containsKey(serverId)) {
				agentServerLinkedSet.put(serverId, new ConcurrentHashMap<>());
			}

			if (agentServerLinkedSet.get(serverId).containsKey(serverIndex)) {
				agentServerLinkedSet.get(serverId).get(serverIndex).addSession(session, linkIndex);
			} else {
				ServerLinkedGroup group = new ServerLinkedGroup(count);
				group.setServerId(serverId);
				group.addSession(session, linkIndex);
				agentServerLinkedSet.get(serverId).put(serverIndex, group);
			}
			return agentServerLinkedSet.get(serverId).get(serverIndex);
		}
	}

	public static void syncCheckMsgToGate(eServerType serverType) {
		LoadProto.CheckManyCrossConnectMsg.Builder builder = LoadProto.CheckManyCrossConnectMsg.newBuilder();
		builder.setServerType(serverType.value);

		if (serverType == eServerType.CROSS) {
			builder.setId(BuiltinPlugin.getCrossServerId());
		} else {
			builder.setId(BuiltinPlugin.getServerId());
		}

		for (Map<Integer, ServerLinkedGroup> map : agentServerLinkedSet.values()) {
			for (Map.Entry<Integer, ServerLinkedGroup> entry : map.entrySet()) {
				ServerLinkedGroup value = entry.getValue();

				AbstractSession[] sessions = value.getSessions();
				if (sessions == null) {
					continue;
				}
				for (int i = 0; i < sessions.length; i++) {
					AbstractSession session = sessions[i];
					if (session == null) {
						continue;
					}
					if (!session.isAlive()) {
						continue;
					}
					builder.setIndex(i);
					PBPacket message = PBPacketUtils.buildMessage(AgentProtocol.A_CROSS_HEART_CHECK, builder);
					session.write(message);
				}
			}
		}
	}

	public static void closeCtxSession(ChannelHandlerContext ctx) {
		ServerLinkedGroup client = getLinkedClient(ctx);
		if (client == null) {
			return;
		}

		if (!agentServerLinkedSet.containsKey(client.getServerId())) {
			return;
		}
		int id = client.getServerIndex();
		logger.warn("removeLinkedClient ---serverId={} removeId : {}", client.getServerId(), id);
		if (agentServerLinkedSet.get(client.getServerId()).containsKey(id)) {
			AbstractSession[] sessions = agentServerLinkedSet.get(client.getServerId()).get(id).getSessions();
			for (AbstractSession session : sessions) {
				logger.warn("s serverId={} id={} remoteAddress: {}, d remoteAddress: {}", client.getServerId(), id,
						ctx.channel().remoteAddress(), ((NettyChannelSession) session).getChannel().remoteAddress());
				if (ctx.channel().remoteAddress()
						.equals(((NettyChannelSession) session).getChannel().remoteAddress())) {
					session.close();
					logger.warn("remove session Address : {}", ctx.channel().remoteAddress());
					break;
				}
			}
			boolean isAllDown = true;
			for (AbstractSession session : sessions) {
				if (session == null) {
					System.out.println("==");
				}
				if (session != null && session.isAlive()) {
					isAllDown = false;
					break;
				}
			}
			if (isAllDown) {
				synchronized (lockObj) {
					agentServerLinkedSet.get(client.getServerId()).remove(id);
				}
				logger.warn("remove client serverId : {}", client.getServerId());
			}
		}
	}
}
